![require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages](https://cdn.sanity.io/images/cgdhsj6q/production/be8ab80c8efa5907bc341c6fefe9aa20d239d890-1600x1097.png?w=400&fit=max&auto=format)
Security News
require(esm) Backported to Node.js 20, Paving the Way for ESM-Only Packages
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
@kitajs/html
Advanced tools
🏛️
KitaJS HTML
KitaJS HTML
is a 0 dependencies, fast and concise HTML generator for JavaScript with JSX syntax.
npm install @kitajs/html # or yarn add @kitajs/html
Install @kitajs/html
with your favorite package manager, import it into the top of your jsx
/tsx
file and change your tsconfig.json to transpile jsx syntax.
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "html.createElement",
"jsxFragmentFactory": "html.Fragment"
}
}
// Always remember to import html from '@kitajs/html'
import html from '@kitajs/html'
// Using as a simple html builder
console.log(<div>Hello World</div>) // '<div>Hello World</div>'
// Maybe your own server-side html frontend
function route(request, response) {
return response
.header('Content-Type', 'text/html')
.send(<div>Hello World</div>)
}
// What about generating a static html file?
fs.writeFileSync(
'index.html',
<html>
<head>
<title>Hello World</title>
</head>
<body>
<div>Hello World</div>
</body>
</html>
)
// Also as a component library
function Layout({ name, children }: html.PropsWithChildren<{ name: string }>) {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
<div>Hello {name}</div>
{children}
</body>
</html>
)
}
console.log(<Layout name="World">I'm in the body!</Layout>)
// Anywhere you want! All JSX becomes a string
typeof (<div>Hello World</div>) === 'string'
This package just provides functions to transpile JSX to a HTML string, you can imagine doing something like this before, but now with type checking and intellisense:
// without @kitajs/html
const html = `<div> Hello World!<div>` ❌
// with @kitajs/html
const html = <div>Hello World!<div> ✅
// Also results into a string, but with type checks.
This package aims to be a HTML builder, not an HTML sanitizer. This means that no HTML content is escaped by default. However we provide a custom attribute called safe
that will sanitize everything inside of it. You can also use the exported html.escapeHtml
function to escape other contents arbitrarily.
// Attributes are always escaped by default
<div style={'"&<>\''}></div> // <div style=""&<>'"></div>
<div style={{ backgroundColor: '"&<>\'' }}></div> // <div style="background-color:"&<>';"></div>
// Correct way to escape input content, you should only use when rendering user input
<div safe>{untrusted}</div> // <div><script>alert("hacked!")</script></div>
// Manual escaping with html.escapeHtml
<div>{'<a></a>' + html.escapeHtml('<a></a>')}</div> // <div><a></a><a></a></div>
// ⚠️ unsafe input is not escaped by default
<div>{untrusted}</div> // <div><script>alert('hacked!')</script></div>
It's like if React's dangerouslySetInnerHTML
was enabled by default.
You should always use the safe
attribute when you are rendering user input. This will sanitize its contents and avoid XSS attacks.
function UserCard({ name, description, date, about }) {
return (
<div class="card">
<h1 safe>{name}</h1>
<br />
<p safe>{description}</p>
<br />
// controlled input, no need to sanitize
<time datetime={date.toISOString()}>{date.toDateString()}</time>
<br />
<p safe>{about}</p>
</div>
)
}
Note that only at the very bottom of the HTML tree is where you should use the safe
attribute, to only escape where its needed.
👉 There's an open issue to integrate this within a typescript plugin to emit warnings and alerts to use the safe attribute everywhere a variable is used. Wanna help? Check this issue.
Migrating from plain HTML to JSX can be a pain to convert it all manually, as you will find yourself hand placing quotes and closing void elements. Luckily for us, there's a tool called htmltojsx that can help us with that.
<!-- Hello world -->
<div class="awesome" style="border: 1px solid red">
<label for="name">Enter your name: </label>
<input type="text" id="name" />
</div>
<p>Enter your HTML here</p>
Generates:
<>
{/* Hello world */}
<div className="awesome" style={{ border: '1px solid red' }}>
<label htmlFor="name">Enter your name: </label>
<input type="text" id="name" />
</div>
<p>Enter your HTML here</p>
</>
When you have static html, is simple to get amazing performances, just save it to a constant and reuse it. However, if you need to hydrate the html with dynamic values in a super fast way, you can use the compile
property to compile the html and reuse it later.
import html from '@kitajs/html'
const compiled = html.compile<['param1', 'param2']>(
<div>
<div>$param1</div>
<div>$param2</div>
<div>$notFound</div>
</div>,
// or
<MyComponent param1="$param1" param2="$param2" />
)
const html = compiled({ param1: 'Hello', param2: 'World!' })
// formatted html to make it easier to read
// <div>
// <div>Hello</div>
// <div>World!</div>
// <div>$notFound</div>
// </div>
This makes the html generation around 1500 times faster than just using normal jsx.
Variables that were not passed to the compile
function are ignored silently, this way you can reuse the result into another compile
function or just because the your "$val
" was supposed to be a static value.
JSX does not allow multiple root elements, but you can use a fragment to group multiple elements:
const html = (
<>
<div>1</div>
<div>2</div>
</>
)
Learn more about JSX syntax here!
All HTML elements and attributes should be supported.
Missing an element or attribute? Please create an issue or a PR to add it. It's easy to add.
tag
tagThe <tag of="">
tag is a custom internal tag that allows you to render any runtime selected tag you want. Possibly reasons to prefer this tag over extending types:
<tag of="asd" />
// <asd></asd>
<tag of="my-custom-KEBAB" />
// <my-custom-KEBAB></my-custom-KEBAB>
We do recommend using extending types instead, as it will give you intellisense and type checking.
Sadly, we cannot allow async components in JSX and keep the same string type for everything else. Even though it should be possible to write async components you will have no real benefit from it, as you will always have to await the whole html generation to complete before you can render it.
You should fetch async data in the following way:
// Fetches all async code beforehand and passes its contents to the component.
async function render(name) {
const data = await api.data(name)
const otherData = await api.otherData(name)
return <Layout data={data} otherData={data} />
}
Just as exemplified above, you may also want to add custom properties to your elements. You can do this by extending the JSX
namespace.
⚠️ Please follow the JSX convention and do not use
kebab-case
for your properties, usecamelCase
instead. We internally transform allcamelCase
properties tokebab-case
to be compliant with the HTML and JSX standards.
declare global {
namespace JSX {
// Adds a new element called mathPower
interface IntrinsicElements {
mathPower: HtmlTag & {
// Changes properties to the math-power element
myExponential: number
// this property becomes the <>{children}</> type
children: number
}
}
// Adds hxBoost property to all elements native elements (those who extends HtmlTag)
interface HtmlTag {
hxBoost: boolean
}
}
}
const element = (
<mathPower myExponential={2} hxBoost>
{3}
</mathPower>
)
// Becomes <math-power my-exponential="2" hx-boost>3</math-power>
This package is just a string builder on steroids, as you can see how this works. However we are running a benchmark with an JSX HTML with about 10K characters to see how it performs.
You can run this yourself by running pnpm bench
.
// Apple M1 Pro 8gb
@kitajs/html:
44 767 ops/s, ±0.17% | 99.91% slower
@kitajs/html - compiled:
48 124 728 ops/s, ±0.48% | fastest
typed-html:
19 199 ops/s, ±0.45% | slowest, 99.96% slower
This package just aims to be a drop in replacement syntax for JSX, and it works because you tell tsc to transpile JSX syntax to calls to our own html
namespace.
<ol start={2}>
{[1, 2].map((i) => (
<li>{i}</li>
))}
</ol>
Gets transpiled by tsc to plain javascript:
html.createElement(
'ol',
{ start: 2 },
[1, 2].map((i) => html.createElement('li', null, i))
)
Which, when called, returns this string:
'<ol start="2"><li>1</li><li>2</li></ol>'
This package emits HTML as a compact string, useful for over the wire environments. However, if your use case really needs the output HTML to be pretty printed, you can use an external JS library to do so, like html-prettify.
import html from '@kitajs/html'
import prettify from 'html-prettify'
const html = (
<div>
<div>1</div>
<div>2</div>
</div>
)
console.log(html)
// <div><div>1</div><div>2</div></div>
console.log(prettify(html))
// <div>
// <div>1</div>
// <div>2</div>
// </div>
👉 There's an open PR to implement this feature natively, wanna work on it? Check this PR.
This repository was initially forked from typed-html and modified to add some features and increase performance.
Initial credits to nicojs and contributors for the amazing work.
Licensed under the Apache License, Version 2.0.
FAQs
Fast and type safe HTML templates using TypeScript.
The npm package @kitajs/html receives a total of 9,670 weekly downloads. As such, @kitajs/html popularity was classified as popular.
We found that @kitajs/html demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
require(esm) backported to Node.js 20, easing the transition to ESM-only packages and reducing complexity for developers as Node 18 nears end-of-life.
Security News
PyPI now supports iOS and Android wheels, making it easier for Python developers to distribute mobile packages.
Security News
Create React App is officially deprecated due to React 19 issues and lack of maintenance—developers should switch to Vite or other modern alternatives.